﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
using NCrawler;
using NCrawler.HtmlProcessor;
using System.Threading;
using NCrawler.Interfaces;
using NCrawler.Services;

namespace OfficeClip.OpenSource.SpellO
{
    public class WebCrawler
    {
        //private static string url = "http://www.officeclip.com";
        //private static int maxCrawlerDepth = 3;
        //private static int threadCount = 1;
        //private static string disallowedUrls = @"(\.jpg|\.css|\.js|\.gif|\.jpeg|\.png)";
        //public static Regex[] disallowedUrls = null;
        //public static List<Result.Output> OutputList;
        //public static string currentUrl = string.Empty;
        public static Mutex mut = new Mutex();
        public static Settings.SpellO spello;
        public static int siteIndex;
        public static Form1 mainForm;

        #region Class Methods

        public static void Run(int siteId, Form1 form)
        {
            mainForm = form;
            spello = Constants.Deserialize();
            siteIndex = siteId;
            #region Not needed logic
            //foreach (Settings.Site site in spello.Sites)
            //{
            //    if (site.Id.ToLower() == siteId.ToLower())
            //    {
            //        siteIndex++;
            //        break;
            //    }
            //    else
            //        siteIndex++;
            //}
            //if (siteIndex == -1)
            //{
            //    throw new Exception("Invalid site as first argument");
            //}
            #endregion
            //OutputList = new List<Result.Output>();
            /*Blag - The following line has blank spaces in the string so that it can handle
             *there being nothing in the exclude box if the string is left empty it will disallow everything.*/
            string disallowedUrls = "                           ";
            if (spello.Sites[siteIndex].Crawler.DisallowedUrls.Count > 0)
            {
                disallowedUrls = spello.Sites[siteIndex].Crawler.DisallowedUrls[0];
                for (int i = 1; i < spello.Sites[siteIndex].Crawler.DisallowedUrls.Count; i++)
                {
                    disallowedUrls = disallowedUrls + "|" + spello.Sites[siteIndex].Crawler.DisallowedUrls[i];
                }
            }
            IFilter[] ExtensionsToSkip = new[]
            {
                (RegexFilter)new Regex(disallowedUrls,
                    RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)
            };

            Crawler c = new Crawler(new Uri(spello.Sites[siteIndex].BaseUri),
                new HtmlDocumentProcessor(), // Process html
                new DumperStep())
                {
                    // Custom step to visualize crawl
                    MaximumThreadCount = spello.Sites[siteIndex].Crawler.ThreadCount,
                    MaximumCrawlDepth = spello.Sites[siteIndex].Crawler.MaxCrawlDepth,
                    //ExcludeFilter = disallowedUrls
                    ExcludeFilter = ExtensionsToSkip
                };

            // Begin crawl
            c.Crawl();
        }

        #endregion
    }

    #region Nested type: DumperStep

    /// <summary>
    /// Custom pipeline step, to dump url to console
    /// </summary>
    internal class DumperStep : NCrawler.Interfaces.IPipelineStep
    {
        #region IPipelineStep Members

        //delegate void MyDelegate(Result.Output output);
        /// <summary>
        /// </summary>
        /// <param name="crawler">
        /// The crawler.
        /// </param>
        /// <param name="propertyBag">
        /// The property bag.
        /// </param>
        public void Process(Crawler crawler, PropertyBag propertyBag)
        {
            List<string> recommends;
            StringBuilder recommendationSb;
            string url = propertyBag.Step.Uri.ToString();
            Result.Output output = null;
            WebCrawler.mut.WaitOne();
            //try
            //{
            output = new OfficeClip.OpenSource.SpellO.Result.Output();
            output.Mistakes = new List<Result.Mistake>();
            output.Url = url;
            Hashtable hRecommend = (new Speller()).GetRecommendations(propertyBag.Text, WebCrawler.spello, WebCrawler.siteIndex);
            foreach (string key in hRecommend.Keys)
            {
                recommendationSb = new StringBuilder();
                if (WebCrawler.spello.Sites[WebCrawler.siteIndex].SpellCheck.ShowSuggestions)
                {
                    recommends = (List<string>)hRecommend[key];
                    foreach (string str in recommends)
                    {
                        if (recommendationSb.Length > 0)
                            recommendationSb.Append(", ");
                        recommendationSb.Append(str);
                    }
                }
                output.Mistakes.Add(new Result.Mistake(key, recommendationSb.ToString()));
            }
            if (WebCrawler.mainForm != null)
            {
                WriteToForm(output);
            }
            WriteToLog(output);
            //}
            //catch (Exception ex)
            //{
            //    throw new Exception(ex.Message);
            //}
            //finally
            //{
            WebCrawler.mut.ReleaseMutex();
            //}
        }

        public void WriteToForm(Result.Output output)
        {
            WebCrawler.mainForm.AddToList(output.Url, true);
            foreach (Result.Mistake mistake in output.Mistakes)
            {
                WebCrawler.mainForm.AddToList("    " + mistake.Error, false);
            }
        }

        public void WriteToLog(Result.Output output)
        {
            Program.log.WriteSubTitle("SpellO Url:", output.Url);
            foreach (Result.Mistake mistake in output.Mistakes)
            {
                Program.log.WriteInfo(mistake.Error, mistake.Suggestions);
            }
            Program.log.Flush();
        }

        #endregion
    }

    #endregion
}
